package tasks

import java.io.BufferedReader
import java.util.regex.Pattern
import java.io.PrintWriter
import java.io.File
import java.util.regex.Matcher
import java.io.FileReader
import enums.TipoDocumento
import enums.Sexo
import enums.Jornada
import enums.Role
import enums.EPState
import enums.CategoriaIndicador
import util.StringUtil
import java.util.Properties
import java.io.FileInputStream
import enums.OSs
import enums.RecipientType

object GenerateModels {

  val enumsClasses = List(classOf[CategoriaIndicador], classOf[TipoDocumento], classOf[Sexo], classOf[Jornada], classOf[Role], classOf[EPState],classOf[OSs],classOf[RecipientType])

  def apply {
    val modelsDir = new File("app/models")
    val enumsDir = new File("app/enums")
    val file = new File("client-app/client/common/Models.java")
    
        val p = new Properties();
    p.load(new FileInputStream("dev-source/tasks.properties"))

    val wsFolder = p.getProperty("android-ws")
    val projectFolder = p.getProperty("android-project")
    val modelsPackage = p.getProperty("android-root-package") + ".models"
    val folder = s"$wsFolder/$projectFolder/src/${modelsPackage.replace(".", "/")}" 
    val modelsRoute = s"$folder/Models.java"
    val enumsRoute = s"$folder/Enums.java"
    
    
    println(s"""Generando modelos a: $modelsRoute
        |Generando enums a: $enumsRoute""".stripMargin)

    val androidFile = new File(modelsRoute)
    val enumsFile = new File(enumsRoute)
    
    if (!file.exists())
      file.createNewFile();
    val clientModels = new PrintWriter(file);

    if (!androidFile.exists())
      androidFile.createNewFile();
    val androidModels = new PrintWriter(androidFile);

    if (!enumsFile.exists())
      enumsFile.createNewFile();
    val androidEnums = new PrintWriter(enumsFile);

    clientModels.println("""
    |package client.common;
    |import org.stjs.javascript.Array;
    |import org.stjs.javascript.annotation.*;
    |import client.common.angular.*;
    |import java.util.Date;

    |@STJSBridge
    |public interface Models{
        """.stripMargin)

    androidModels.println(s"""
    |package $modelsPackage;
    |import java.util.Date;
    |import java.util.List;
    |import java.util.Set;
    |import java.io.Serializable;
        
    |import $modelsPackage.Enums.*;
    
    |public interface Models{
        """.stripMargin)
    recursive(modelsDir).foreach { f =>

      clientModels.println(process(f))
      androidModels.println(androidProcess(f))
      println("procesado modelo " + f.getName())
    }
    clientModels.println("}")
    androidModels.println("}")

    androidEnums.println(s"""
        |package $modelsPackage;

        |public interface Enums {
        """.stripMargin)
 
    enumsClasses.foreach { clazz =>
      androidEnums.println(s"""
      |	public enum ${clazz.getSimpleName()} {
      |		${clazz.getEnumConstants().map(value => value.name()).mkString(",")}
      |	}""".stripMargin)

    }
    androidEnums.println("}")

    clientModels.close();
    androidModels.close();
    androidEnums.close();
  }

  def recursive(file: File): Array[File] = {
    val these = file.listFiles.filter(f => f.getName().endsWith(".java") || f.isDirectory());
    these.filter(!_.isDirectory()) ++ these.filter(_.isDirectory).flatMap(recursive)
  }

  def process(file: File): String = {
    val br = new BufferedReader(new FileReader(file));
    val sb = new StringBuffer

    var line = nextLine(br);

    var flag = false

    while (line.isDefined && !flag) {
      val lline = line.get;
      if (lline.startsWith("public class")) {
        sb.append("	@SyntheticType\n")
        sb.append(s"	public static class ${file.getName().replace(".java", "")} extends Promise{\n");
        line = nextLine(br);
        flag = true
      }
      line = nextLine(br);

    }

    while (line.isDefined) {
      val enums = enumsClasses.map(c => c.getSimpleName()).mkString("|")
      val lline = line.get.replaceAll(enums, "String");
      val matcher = pattern.matcher(lline);

      if (matcher.matches()) {
        val fieldType = matcher.group(1).replaceAll("(List|Set)\\s*<", "Array<")
        sb.append(s"		public $fieldType ${matcher.group(2)}; \n")
      }
      line = nextLine(br);
    }
    if (flag)
      sb.append("""
    		  |	}
          """.stripMargin)
    br.close();
    return sb.toString();
  }

  def androidProcess(file: File): String = {
    val br = new BufferedReader(new FileReader(file));
    val sb = new StringBuffer

    var line = nextLine(br);

    var flag = false

    while (line.isDefined && !flag) {
      val lline = line.get;
      if (lline.startsWith("public class")) {
        sb.append(s"""	public static class ${file.getName().replace(".java", "")} implements Serializable {
        |		public static final long serialVersionUID = 1l;
        """.stripMargin);
        line = nextLine(br);
        flag = true
      }
      line = nextLine(br);
    }

    while (line.isDefined) {
      val lline = line.get
      val matcher = pattern.matcher(lline);

      if (matcher.matches()) {
        writeJavaMember(matcher, sb)
      }
      line = nextLine(br);
    }
    if (flag)
      sb.append("""
    		  |	}
          """.stripMargin)

    br.close();
    return sb.toString();
  }

  def writeJavaMember(matcher: Matcher, sb: StringBuffer) {
    val fieldType = matcher.group(1)
    val fieldName = matcher.group(2)
    if (fieldName.contains("serialVersionUID"))
      return ;
    sb.append(s"""		
    		|		private $fieldType $fieldName;
            
            |		public $fieldType get${StringUtil.firstToUpper(fieldName)} () {
        	|			return $fieldName;
        	|		}
        	
        	|		public void set${StringUtil.firstToUpper(fieldName)} ($fieldType value) {
        	|			$fieldName = value;
        	|		}
        """.stripMargin)

  }
  def main(args: Array[String]) {
    GenerateModels.apply

    //    val x = pattern.matcher("POST     /api/login                                                                                          controllers.UserCtrl.login()");
    //    println(x.matches());
  }
  def nextLine(br: BufferedReader): Option[String] = {
    try {
      var line = br.readLine().trim();
      while (line.length() == 0)
        line = br.readLine().trim();

      Some(line)

    } catch {
      case _: Throwable => None
    }
  }

  val pattern = Pattern.compile("^\\s*private\\s+(.+?)\\s+(.+?);\\s*$");

}
